package runtime

import (
	"kumachan/interpreter/core"
	"kumachan/interpreter/program"
)


type runtime struct {
	program    *program.Program
	environ    *Environment
	library    *Library
	workers    *Workers
	eventloop  *core.EventLoop
}
type Library struct {
	NativeFunctions  map[string] core.NativeFunction
}
type Workers struct {
	// TODO
}

type runtimeHandle struct {
	Runtime    *runtime
	FrameInfo  core.FrameInfo
}

type Environment struct {
	Args  [] string
	// TODO
}
type ExitSignal chan struct{}


func Run(p *program.Program, ns string, env *Environment, exit ExitSignal) (core.RuntimeHandle, bool) {
	var eventloop = core.CreateEventLoop()
	var workers = &Workers {
		// TODO
	}
	var r = &runtime {
		program:   p,
		environ:   env,
		library:   defaultLibrary,
		workers:   workers,
		eventloop: eventloop,
	}
	var h = makeRuntimeHandle(r)
	var entry, has_entry = p.Executable.LookupEntry(ns)
	if has_entry {
		var ctx = program.CreateEvalContext(h)
		var entry_obj = (program.CallFunRef { Callee: entry }).Eval(ctx)
		var entry_o = (*entry_obj).(core.Observable)
		go (func() {
			var sub_error = make(chan error, 1)
			var sub_terminate = make(chan bool)
			core.Run(entry_o, eventloop, core.DataSubscriber {
				Error:     sub_error,
				Terminate: sub_terminate,
			})
			var ok = <- sub_terminate
			if ok {
				close(exit)
			} else {
				var e = <- sub_error
				core.Crash(h, core.ErrorInEntryObservable, e.Error())
			}
		})()
	}
	return h, has_entry
}

func makeRuntimeHandle(r *runtime) core.RuntimeHandle {
	return &runtimeHandle {
		Runtime: r,
	}
}
func (h *runtimeHandle) WithFrameInfo(info core.FrameInfo) core.RuntimeHandle {
	return &runtimeHandle {
		Runtime:   h.Runtime,
		FrameInfo: info,
	}
}
func (h *runtimeHandle) GetFrameInfo() core.FrameInfo {
	return h.FrameInfo
}
func (h *runtimeHandle) EventLoop() *core.EventLoop {
	return h.Runtime.eventloop
}
func (h *runtimeHandle) LibraryNativeFunction(id string) core.NativeFunction {
	var f, exists = h.Runtime.library.NativeFunctions[id]
	if !(exists) {
		var msg = ("no such native function: " + id)
		core.Crash(h, core.MissingNativeFunction, msg)
	}
	return f
}


